#include <tinx/global.h>
#include <tinx/string.h>
#include <tinx/assert.h>

typedef struct descriptor_t
{
    u16 limit_low;
    u32 base_low : 24;
    u8 type : 4;
    u8 segment : 1;
    u8 dpl : 2;
    u8 present : 1;
    u8 limit_high : 4;
    u8 RESERVED : 1;
    u8 long_mode : 1;
    u8 big : 1;
    u8 granularity : 1;
    u8 base_high;
} _packed descriptor_t;

#define GDT_SIZE 256

static pointer_t gdt_ptr;
static descriptor_t gdt[GDT_SIZE];

u32 get_cr0()
{
    asm volatile(
        "movl %cr0, %eax\n");
}

void set_cr0(u32 cr0)
{
    asm volatile(
        "movl %%eax, %%cr0\n" :: "a"(cr0));
}

void descriptor_init(descriptor_t *desc, u32 base, u32 limit)
{
    desc->base_low = base & 0xffffff;
    desc->base_high = (base >> 24) & 0xff;
    desc->limit_low = limit & 0xffff;
    desc->limit_high = (limit >> 16) & 0xf;
}

void global_init()
{
    memset(gdt, 0, sizeof(gdt));

    descriptor_t *desc = gdt + KERNEL_CODE_IDX;
    descriptor_init(desc, 0, 0xfffff);
    desc->big = 1;
    desc->dpl = 0b00;
    desc->granularity = 1;
    desc->long_mode = 0;
    desc->present = 1;
    desc->segment = 1;
    desc->type = 0b1010;

    desc = gdt + KERNEL_DATA_IDX;
    descriptor_init(desc, 0, 0xfffff);
    desc->big = 1;
    desc->dpl = 0b00;
    desc->granularity = 1;
    desc->long_mode = 0;
    desc->present = 1;
    desc->segment = 1;
    desc->type = 0b0010;

    desc = gdt + USER_CODE_IDX;
    descriptor_init(desc, 0, 0xfffff);
    desc->big = 1;
    desc->dpl = 3;
    desc->granularity = 1;
    desc->long_mode = 0;
    desc->present = 1;
    desc->segment = 1;
    desc->type = 0b1010;

    desc = gdt + USER_DATA_IDX;
    descriptor_init(desc, 0, 0xfffff);
    desc->big = 1;
    desc->dpl = 3;
    desc->granularity = 1;
    desc->long_mode = 0;
    desc->present = 1;
    desc->segment = 1;
    desc->type = 0b0010;

    gdt_ptr.offset = (u32)gdt;
    gdt_ptr.size = sizeof(gdt) - 1;
    asm volatile("lgdt gdt_ptr");
}

tss_t tss;
pointer_t tss_ptr;

void tss_init()
{
    memset(&tss, 0, sizeof(tss));
    tss.iobase = sizeof(tss);
    tss.ss0 = KERNEL_DATA_SELECTOR;

    descriptor_t *desc = gdt + KERNEL_TSS_IDX;
    descriptor_init(desc, (u32)&tss, sizeof(tss) - 1);
    desc->segment = 0;     // 系统段
    desc->granularity = 0; // 字节
    desc->big = 0;         // 固定为 0
    desc->long_mode = 0;   // 固定为 0
    desc->present = 1;     // 在内存中
    desc->dpl = 0;         // 用于任务门或调用门
    desc->type = 0b1001;   // 32 位可用 tss

    asm volatile("ltr %%ax\n" :: "a"(KERNEL_TSS_SELECTOR));
}

void tls_update(task_t *task)
{
    tls_t *tls = task->tls;
    u32 addr = tls->addr;
    descriptor_t *desc = gdt + TLS_IDX;
    descriptor_init(desc, addr, (tls->size - 1) >> 12);
}

void tls_init()
{
    descriptor_t *desc = gdt + TLS_IDX;
    memset(desc, 0, sizeof(descriptor_t));
    descriptor_init(desc, 0, 0);
    desc->present = 1;     // 在内存中
    desc->segment = 1;     // 代码 / 数据段
    desc->dpl = 3;         // 用户级
    desc->granularity = 1; // 4 KB
    desc->big = 1;         // 32 位
    desc->long_mode = 0;   // 非长模式
    desc->type = 0b0010;   // 数据段
}